﻿using System;
using System.Data.Entity;
using System.Linq;
using ITTestingSystem.BusinessLogic.DataServices.Abstract;

namespace ITTestingSystem.BusinessLogic.DataServices.Concrete
{
    /// <summary>
    /// 
    /// </summary>
    /// 
    /// <typeparam name="T">
    /// 
    /// </typeparam>
    /// 
    public class RepositoryForEntity<T> : IRepositoryForEntity<T>, IDisposable where T : class
    {
        #region Реалізація IRepositoryForEntity

        /// <summary>
        /// 
        /// </summary>
        /// 
        private readonly DbContext _db;

        /// <summary>
        /// 
        /// </summary>
        /// 
        private readonly DbSet<T> _entitySet;

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <param name="dbContext">
        /// 
        /// </param>
        /// 
        public RepositoryForEntity(DbContext dbContext)
        {
            if (dbContext == null)
            {
                throw new NullReferenceException();
            }

            _db = dbContext;

            //TODO: Перевірити випадок, коли потрібний instance відсутній

            _entitySet = dbContext.Set<T>();
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <param name="entity">
        /// 
        /// </param>
        /// 
        void IRepositoryForEntity<T>.Add(T entity)
        {
            if (IsNull(entity))
            {
                throw new ArgumentNullException();
            }

            _entitySet.Add(entity);
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <returns>
        /// 
        /// </returns>
        /// 
        IQueryable<T> IRepositoryForEntity<T>.GetAll()
        {
            return _entitySet;
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <param name="id">
        /// 
        /// </param>
        /// 
        /// <returns>
        /// 
        /// </returns>
        /// 
        T IRepositoryForEntity<T>.GetById(int id)
        {
            return _entitySet.Find(id);
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <param name="entity">
        /// 
        /// </param>
        /// 
        void IRepositoryForEntity<T>.Update(T entity)
        {
            if (IsNull(entity))
            {
                throw new ArgumentNullException();
            }

            _db.Entry(entity).State = EntityState.Modified;
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <param name="id">
        /// 
        /// </param>
        /// 
        void IRepositoryForEntity<T>.DeleteById(int id)
        {
            var entity = _entitySet.Find(id);

            if (entity == null)
            {
                return;
            }

            _entitySet.Remove(entity);
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <param name="entity">
        /// 
        /// </param>
        /// 
        void IRepositoryForEntity<T>.Delete(T entity)
        {
            if (IsNull(entity))
            {
                throw new ArgumentNullException();
            }

            if (_db.Entry(entity).State == EntityState.Detached)
            {
                _entitySet.Attach(entity);
            }

            _entitySet.Remove(entity);
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        void IRepositoryForEntity<T>.Save()
        {
            _db.SaveChanges();
        }

        #endregion

        private static bool IsNull(object parameter)
        {
            return parameter == null;
        }

        #region Реалізація IDisposible

        /// <summary>
        /// 
        /// </summary>
        /// 
        private bool _disposed;

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <param name="disposing">
        /// 
        /// </param>
        /// 
        protected virtual void Dispose(bool disposing)
        {
            if (_disposed)
            {
                return;
            }

            if (disposing)
            {
                _db.Dispose();
            }

            _disposed = true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}
